#region Copyright Jonathan Quiros

//	
// All rights are reserved. Reproduction or transmission in whole or in 
// part, in any form or by any means, electronic, mechanical or otherwise, 
// is prohibited without the prior written consent of the copyright owner.
//
// Filename: ObjectContextExtensions.cs
//

#endregion

using System;
using System.Data;
using System.Data.Metadata.Edm;
using System.Data.Objects;
using System.Data.Objects.DataClasses;
using System.Linq;

namespace Novagoal.Repositories.Extensions
{
    public static class ObjectContextExtensions
    {
        #region CONSTANTS



        #endregion

        #region PROPERTIES

        

        #endregion

        #region PUBLIC METHODS

        /// <summary>
        /// <para>
        /// Attaches an object or object graph to the object context in the entity set of the object if the object
        /// is not already attached.
        /// </para>
        /// </summary>
        /// <typeparam name="TEntityWithKey">The type of the object to attach.</typeparam>
        /// <param name="objectContext">The object context where the object will be attached.</param>
        /// <param name="entityWithKey">The object to attach. Must implement IEntityWithKey.</param>
        /// <returns>The <paramref name="entityWithKey"/> attached to the <paramref name="objectContext"/>.</returns>
        public static TEntityWithKey AttachTo<TEntityWithKey>(this ObjectContext objectContext, TEntityWithKey entityWithKey)
            where TEntityWithKey : IEntityWithKey
        {
            var entitySetName = GetEntitySetFullName(objectContext, entityWithKey);
            var entityKey = objectContext.CreateEntityKey(entitySetName, entityWithKey);
            ObjectStateEntry objectStateEntry;

            //The entity is assume as detached, unless its state says otherwise.
            var isDetached = true;

            if (objectContext.ObjectStateManager.TryGetObjectStateEntry(entityKey, out objectStateEntry))
            {
                entityWithKey = (TEntityWithKey)objectStateEntry.Entity;
                isDetached = objectStateEntry.State == EntityState.Detached;
            }

            if (isDetached)
            {
                objectContext.AttachTo(entitySetName, entityWithKey);
            }

            return entityWithKey;
        }

        /// <summary>
        /// Returns the full name of the entity set of an object.
        /// </summary>
        /// <param name="objectContext">the object context where the entity set is defined</param>
        /// <param name="entityWithKey">the object that belongs to the entity set</param>
        /// <returns>An string with the full name of an entity set.</returns>
        /// <exception cref="ArgumentNullException"><paramref name="objectContext"/> is null</exception>
        /// <exception cref="ArgumentNullException"><paramref name="entityWithKey"/> is null</exception>
        private static string GetEntitySetFullName(this ObjectContext objectContext, IEntityWithKey entityWithKey)
        {
            if (objectContext == null)
            {
                throw new ArgumentNullException("objectContext");
            }

            if (entityWithKey == null)
            {
                throw new ArgumentNullException("entityWithKey");
            }

            // If the EntityKey exists, simply get the Entity Set name from the key
            if (entityWithKey.EntityKey != null)
            {
                return entityWithKey.EntityKey.EntitySetName;
            }

            var entityTypeName = entityWithKey.GetType().Name;
            var container = objectContext.MetadataWorkspace.GetEntityContainer(objectContext.DefaultContainerName, DataSpace.CSpace);

            var entitySetName = container.BaseEntitySets
                .Where(meta => meta.ElementType.Name == entityTypeName)
                .Select(meta => meta.Name)
                .First();

            return container.Name + "." + entitySetName;
        }

        #endregion

        #region PRIVATE METHODS



        #endregion
    }
}